/////////////////////////////////////////////////////////////////////////////
// CAdapterManager class implements an adapters container and monitors 
// for a new network adapters.
//
// Version 1.0 - 15 june 2007
//
// Copyright (c) 2007 by FlexByte Software (http://flexbyte.com)
// All rights reserved.
//
// You may use the source code in any product (freeware, commercial, etc.). 
// You may modify the source code as you want except 
// you cannot modify the copyright details at the top of each module.
// If you have a web site or blog give a link to our web site, flexbyte.com
/////////////////////////////////////////////////////////////////////////////
#include <memory>
#include "AdapterManager.h"
#ifndef __IPHLPAPI_H__
	#include <ntddndis.h>
	#include <iphlpapi.h>
	#pragma comment( lib, "iphlpapi.lib" )
	#pragma message( "   Automatically linking with iphlpapi library" )
#endif // __IPHLPAPI_H__
using namespace std;
/////////////////////////////////////////////////////////////////////////////
// CInterfaceEnumerator
/////////////////////////////////////////////////////////////////////////////
CInterfaceEnumerator::CInterfaceEnumerator()
{
	char szErrorMsg[ PCAP_ERRBUF_SIZE ];
	int rc = pcap_findalldevs( &m_devs, szErrorMsg );
	if( rc )
		throw CFlexException( szErrorMsg, rc, true );
	for(pcap_if_t * dev = m_devs; dev != NULL; dev = dev->next)
		push_back( dev );
}
/////////////////////////////////////////////////////////////////////////////
CInterfaceEnumerator::~CInterfaceEnumerator()
{
	if( m_devs )
		pcap_freealldevs( m_devs );
	m_devs = NULL;
}
/////////////////////////////////////////////////////////////////////////////
// CAdapterManager
/////////////////////////////////////////////////////////////////////////////
CAdapterManager::CAdapterManager()
: m_pListener( NULL )
, m_bWork( false )
{
}
/////////////////////////////////////////////////////////////////////////////
CAdapterManager::~CAdapterManager()
{
	Release();
}
void CAdapterManager::SetName()
{
	THREADNAME_INFO info;
	info.dwType = 0x1000;
	info.szName = "AdapterManagerThread";
	info.dwThreadID = -1;
	info.dwFlags = 0;

	__try
	{
		RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD),(DWORD*)&info );
	}
	__except (EXCEPTION_CONTINUE_EXECUTION)
	{
	}
}
/////////////////////////////////////////////////////////////////////////////
CAdapterManager & CAdapterManager::GetInstance()
{
	static auto_ptr< CAdapterManager > theManager( 
		new CAdapterManager 
		);
	return *theManager;
}
/////////////////////////////////////////////////////////////////////////////
void CAdapterManager::Capture(IAdapterListener * pListener)
{
	assert( pListener );
	m_pListener = pListener;

	// Open all adapters
	RefreshAdapters();

	for(int i = 0; i < 2; i ++)
	{
		m_hEvents[ i ] = ::CreateEvent( NULL, TRUE, FALSE, NULL );
		if( !m_hEvents[ i ] )
			throw CFlexException( ::GetLastError() );
	}
	// Run adapters monitor
	m_threadMon.Execute( this );
}
/////////////////////////////////////////////////////////////////////////////
void CAdapterManager::Release()
{
	for(UINT i = 0; i < m_arAdapters.size(); i ++)
	{
		CAdapter * pAdapter = m_arAdapters.at( i );
		if( pAdapter )
			delete pAdapter;
	}
	m_arAdapters.clear();
	m_threadMon.Exit();
}
/////////////////////////////////////////////////////////////////////////////
bool CAdapterManager::IsLocalIp(DWORD dwHostAddr)
{
	CFlexAutoLock lock( &m_lock );
	if( m_mapLocalAddress.find( dwHostAddr ) == m_mapLocalAddress.end() )
		return false;
	return true;
}
/////////////////////////////////////////////////////////////////////////////
CAdapter * CAdapterManager::Get(DWORD dwHostAddr)
{
	CFlexAutoLock lock( &m_lock );
	CIpAddressMap::iterator item = m_mapLocalAddress.find( dwHostAddr );
	return ( item == m_mapLocalAddress.end() ) ? NULL : item->second;
}
/////////////////////////////////////////////////////////////////////////////
void CAdapterManager::Execute()
{
	OVERLAPPED ov = { 0 };
	ov.hEvent = m_hEvents[ 1 ];

	HANDLE hMonitor;
	m_bWork = true;
	while( IsWorking() )
	{
		try
		{
			DWORD dwResult = NotifyAddrChange( &hMonitor, &ov );
			if( dwResult != ERROR_IO_PENDING)
				throw CFlexException( dwResult );

			dwResult = ::WaitForMultipleObjects( 2, m_hEvents, FALSE, INFINITE );
			if( dwResult == WAIT_OBJECT_0 )
				break;

			// Open all adapters
			RefreshAdapters();
		}
		catch(const CFlexException & e)
		{
			if( m_pListener )
				m_pListener->AnalyzeError( e );
			m_bWork = false;
		}
	}
	m_bWork = false;
	::CloseHandle( m_hEvents[ 1 ] );   
	::CloseHandle( m_hEvents[ 0 ] );
	TRACE( "[THREAD EXIT] CAdapterManager::Execute()\n" );
}
/////////////////////////////////////////////////////////////////////////////
void CAdapterManager::Break()
{
	m_bWork = false;
	::SetEvent( m_hEvents[ 0 ] );
	m_threadMon.Wait();
}
/////////////////////////////////////////////////////////////////////////////
CAdapter * CAdapterManager::InsertAdapter(const char * szDeviceName)
{
	CAdapter * pAdapter = NULL;
	// First, find adapter
	for(UINT i = 0; i < m_arAdapters.size(); i ++)
	{
		CAdapter * pDev = m_arAdapters.at( i );
		string sName( pDev->GetName() );
		if( sName.compare( szDeviceName ) == 0 )
		{
			if( pDev->IsWorking() ) 
				return pDev;
			pAdapter = pDev;
			break;
		}
	}
	// Create adapter
	if( !pAdapter )
	{
		pAdapter = new CAdapter( m_pListener );
		m_arAdapters.push_back( pAdapter );
	}
	// Open adapter
	m_pListener->OpenAdapter( *pAdapter, szDeviceName );
	return pAdapter;
}
/////////////////////////////////////////////////////////////////////////////
void CAdapterManager::RefreshAdapters()
{
	// Open all adapters we have
	Lock();
	m_mapLocalAddress.clear();
	CInterfaceEnumerator ifEnum;
	for(UINT i = 0; i < ifEnum.size(); i ++)
	{
		pcap_if_t * ifItem = ifEnum[ i ];
		if( !ifItem->addresses )
			continue;
		CAdapter * pAdapter = InsertAdapter( ifItem->name );

		// Store local addresses to map
		pcap_addr_t * addr = ifItem->addresses;
		while( addr )
		{
			DWORD dwLocalIp = 
				( ( sockaddr_in * )addr->addr )->sin_addr.s_addr;
			m_mapLocalAddress[ dwLocalIp ] = pAdapter;
			addr = addr->next;
		}
	}
	Unlock();

	// Close non active adapters
	CAdapterArray::iterator iter;
	for(iter = m_arAdapters.begin(); iter != m_arAdapters.end(); iter ++)
	{
		CAdapter * pAdapter = ( *iter );

		bool bFound = false;
		for(UINT n = 0; n < ifEnum.size(); n ++)
		{
			if( !ifEnum[ n ]->addresses )
				continue;
			string sName( ifEnum[ n ]->name );
			if( sName.compare( pAdapter->GetName() ) == 0 )
				bFound = true;
		}
		if( !bFound )
		{
			pAdapter->Close();
		}
	}
}
/////////////////////////////////////////////////////////////////////////////